/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef DSO_DYLD

#include <mach-o/dyld.h>

#ifdef __bool_true_false_are_defined
/* We define these differently than stdbool.h, so ignore the defs there */
#undef bool
#undef true
#undef false
#endif


/* Print an error message and abort all if a specified symbol wasn't found */
static void nosymbol(const char *s)
{
    log_error("Cannot find symbol '%s' in library", s);
    abort();
}

/* We found two symbols for the same name in two different modules */
static NSModule multiple(NSSymbol s, NSModule om, NSModule nm)
{
    NSModule ret = nm;

    log_debug("Symbol \"%s\" found in modules \"%s\" and \"%s\" (using %s)",
              NSNameOfSymbol(s), NSNameOfModule(om), NSNameOfModule(nm),
              NSNameOfModule(ret));

    return (ret);
}

/* We got an error while linking a module, and if it's not a warning we have
   to abort the whole program */
static void linkedit(NSLinkEditErrors category, int number, const char *file,
                     const char *message)
{
    log_error("Errors during link edit of file \"%s\" (error=%d): %s", file,
              number, message);
    /* Check if this error was only a warning */
    if (category != NSLinkEditWarningError) {
        log_error("Cannot continue");
        abort();
    }
}

/* Initialize all DSO stuff */
bool dso_init()
{
    NSLinkEditErrorHandlers h;

    h.undefined = nosymbol;
    h.multiple = multiple;
    h.linkEdit = linkedit;

    NSInstallLinkEditErrorHandlers(&h);
    return (true);
}

/* Attempt to link a library from a specified filename */
dso_handle dso_link(const char *path)
{
    /* We need to load the library publically as NSModuleFileImage is not
       yet implemented (at least for non MH_BUNDLE libraries */
    if (NSAddLibrary(path) != TRUE)
        return (NULL);
    /* We need to return a non-null value, even if it has no meaning. One day
       this whole crap will be fixed */
    return ((void *)!NULL);
}

/* Attempt to unload a library */
bool dso_unlink(dso_handle libr)
{
    /* Check the handle */
    if (libr == NULL) {
        log_error("Attempting to unload a module without handle");
        return (false);
    }

    /* We don't have a module, so, we don't really have to do anything */
    return (true);
}

/* Get the address for a specifed symbol */
void *dso_symbol(dso_handle hdl, const char *nam)
{
    NSSymbol sym = NULL;
    NSModule mod = NULL;
    char *und = NULL;
    void *add = NULL;
    int x = 0;

    /* Check parameters */
    if (hdl == NULL) {
        log_error("Invalid library handler specified");
        return (NULL);
    }

    if (nam == NULL) {
        log_error("Invalid symbol name specified");
        return (NULL);
    }

    /* Process the correct name (add a _ before the name) */
    while (nam[x] != '\0')
        x++;
    und = (char *)malloc(sizeof(char) * (x + 2));
    while (x >= 0)
        und[x + 1] = nam[x--];
    und[0] = '_';

    /* Find the symbol */
    sym = NSLookupAndBindSymbol(und);
    free(und);
    if (sym == NULL)
        return (NULL);

    /* Dump some debugging output since this part is shaky */
    mod = NSModuleForSymbol(sym);
    add = NSAddressOfSymbol(sym);
    log_debug("Symbol \"%s\" found in module \"%s\" at address \"0x%08X\"",
              NSNameOfSymbol(sym), NSNameOfModule(mod), add);

    /* We want to return the address of the symbol */
    return (add);
}

/* Return the error message from dlopen: Well we already print it */
char *dso_error()
{
    return ("no additional message");
}

#endif /* ifdef DSO_DYLD */
